#include "thread.h"
#include "stdint.h" 
#include "string.h"
#include "global.h"
#include "../kernel/memory.h"
#include "interrupt.h"
#include "debug.h"
#include "print.h"


struct task_struct* main_thread;
struct list thread_ready_list;
struct list thread_all_list;
static struct list_elem* thread_tag;

extern void switch_to(struct task_struct* cur, struct task_struct* next);

struct task_struct* running_thread(){
    uint32_t esp;
    asm( "mov %%esp, %0" : "=g" (esp) );
    return (struct task_struct*) (esp & 0xfffff000);
}

static void kernel_thread(thread_func* function, void* func_arg){
    intr_enable();
    function(func_arg);
}

void thread_create(struct task_struct* pthread, thread_func function, void* func_arg){
    pthread->self_kstack -= sizeof(struct intr_stack);      // 预留 中断栈 的空间
    pthread->self_kstack -= sizeof(struct thread_stack);    // 预留 线程栈 的空间
    struct thread_stack* kthread_stack = (struct thread_stack*)pthread->self_kstack; 
    kthread_stack->eip = kernel_thread;
    kthread_stack->function = function;
    kthread_stack->func_arg = func_arg;
    kthread_stack->ebp = kthread_stack->ebx = kthread_stack->esi = kthread_stack->edi = 0;
}

void init_thread(struct task_struct* pthread, char* name, int prio){
    memset(pthread, 0, sizeof(*pthread));
    strcpy(pthread->name, name);

    if( pthread == main_thread ) { pthread->status = TASK_RUNNING; }
    else { pthread->status = TASK_READY; }

    pthread->priority = prio;
    pthread->self_kstack = (uint32_t*) ( (uint32_t)pthread + PG_SIZE );
    pthread->ticks = prio;
    pthread->elapsed_ticks = 0;
    pthread->pgdir = NULL;
    pthread->stack_magic = MAGIC;
}


struct task_struct* thread_start(char* name, int prio, thread_func function, void* func_arg){
    struct task_struct* thread = get_kernel_pages(1);
    init_thread(thread, name, prio);
    thread_create(thread, function, func_arg);

    ASSERT( !list_elem_find(&thread_ready_list, &thread->general_tag) );
    list_append(&thread_ready_list, &thread->general_tag);

    ASSERT( !list_elem_find(&thread_all_list, &thread->all_list_tag) );
    list_append(&thread_all_list, &thread->all_list_tag);

    // asm volatile ( "movl %0, %%esp; pop %%ebp; pop %%ebx; pop %%edi; pop %%esi; ret":: "g"(thread->self_kstack): "memory");
    return thread;
}

static void make_main_thread(){
    main_thread = running_thread();
    init_thread(main_thread, "main", 31);
    
    ASSERT(!list_elem_find(&thread_all_list, &main_thread->all_list_tag));
    list_append(&thread_all_list, &main_thread->all_list_tag);
}


void schedule(){
    ASSERT( intr_get_status() == INTR_OFF );
    struct task_struct* cur = running_thread();
    if(cur->status == TASK_RUNNING){
        ASSERT( !list_elem_find(&thread_ready_list, &cur->general_tag) );
        list_append(&thread_ready_list, &cur->general_tag);
        cur->ticks = cur->priority;
        cur->status = TASK_READY;    // 就绪态
    } else {
        // 如果当前进程被阻塞了，就得等待别的事件来解救它
        // 不把它放回就绪队列
    }

    ASSERT(!list_is_empty(&thread_ready_list));
    thread_tag = NULL; // habit of original author..., no need to assign NULL
    thread_tag = list_pop_front(&thread_ready_list);

    struct task_struct* next = elem2entry(struct task_struct, general_tag, thread_tag);
    next->status = TASK_RUNNING;
    switch_to(cur, next);
}

void thread_module_init(){
    put_str("thread_init start\n");
    list_init(&thread_ready_list);
    list_init(&thread_all_list);
    make_main_thread();
    put_str("thread_init done\n");
}


// block current thread, set thread status to stat
void thread_block(enum task_status stat){
    ASSERT( (stat==TASK_BLOCKED||(stat==TASK_WAITING)||(stat==TASK_HANGNG)));
    enum intr_status old_status = intr_disable();
    struct task_struct* cur_thread = running_thread();
    cur_thread->status = stat;
    schedule();
    intr_set_status(old_status);
}


void thread_unblock(struct task_struct* pthread){
    enum intr_status old_status = intr_disable();
    ASSERT( (pthread->status == TASK_BLOCKED) || (pthread->status == TASK_WAITING) || (pthread->status == TASK_HANGNG) );
    if(pthread->status != TASK_READY ){
        ASSERT(!list_elem_find(&thread_ready_list, &pthread->general_tag));
        if( list_elem_find(&thread_ready_list, &pthread->general_tag) ){
             PANIC("thread_unblock: blocked thread already in ready_list !\n"); 
        }
        list_push_front(&thread_ready_list, &pthread->general_tag);
        pthread->status = TASK_READY;
    }
    intr_set_status(old_status);
}

